<template>
  <el-form v-bind="{ ...getProps }" ref="formElRef" :model="formModel">
    <el-row>
      <template v-for="(schema, index) in getSchema" :key="index">
        <FormItem
          :formActionType="formActionType"
          :schema="schema"
          :formProps="getProps"
          :allDefaultValues="defaultValueRef"
          :formModel="formModel"
          :setFormModel="setFormModel"
        >
          <template #[item]="data" v-for="item in Object.keys($slots)">
            <slot :name="item" v-bind="data"></slot>
          </template>
        </FormItem>
      </template>
      <FormAction
        v-bind="{ ...getProps, ...advanceState }"
        @toggle-advanced="handleToggleAdvanced"
        @reset-action="resetFields"
        @submit-action="submit"
      >
        <template
          #[item]="data"
          v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
        >
          <slot :name="item" v-bind="data"></slot>
        </template>
      </FormAction>
      <slot name="formFooter"></slot>
    </el-row>
  </el-form>
</template>
<script lang="ts">
import { Nullable, Recordable } from '@/global'
import { deepMerge } from '@/utils'
import { computed, defineComponent, onMounted, reactive, Ref, ref, unref, watch } from 'vue'
import FormItem from './components/BasicFormItem.vue'
import FormAction from './components/FormAction.vue'
import { ElRow, ElForm } from 'element-plus'
import { basicProps } from './props'
import { FormActionType, FormProps, FormSchema } from './types/form'
import { AdvanceState } from './types/formAction'
import { useFormValues } from './hooks/useFormValues'
import { useFormEvents } from './hooks/useFormEvents'
import useAdvanced from './hooks/useAdvanced'

export default defineComponent({
  emits: ['advanced-change', 'reset', 'submit', 'register'],
  components: { FormItem, FormAction, ElRow, ElForm },
  props: basicProps,
  setup(props, { emit }) {
    // 表单
    const formModel = reactive<Recordable>({})
    const formElRef = ref<Nullable<FormActionType>>(null)
    const defaultValueRef = ref<Recordable>({})
    function setFormModel(key: string, value: any) {
      formModel[key] = value
    }
    // 传参
    const propsRef = ref<Partial<FormProps>>({})
    const getProps = computed(
      (): FormProps => {
        return { ...props, ...unref(propsRef) } as FormProps
      }
    )
    console.log(getProps)
    async function setProps(formProps: Partial<FormProps>): Promise<void> {
      propsRef.value = deepMerge(unref(propsRef) || {}, formProps)
    }
    // 表单配置
    const schemaRef = ref<Nullable<FormSchema[]>>(null)
    const getSchema = computed((): FormSchema[] => {
      const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any)
      return schemas as FormSchema[]
    })
    const { initDefault } = useFormValues({
      defaultValueRef,
      getSchema,
      formModel
    })
    // 操作配置
    const advanceState = reactive<AdvanceState>({
      isAdvanced: true,
      actionSpan: 0
    })
    const { handleToggleAdvanced } = useAdvanced({
      advanceState,
      getSchema,
      emit,
      getProps
    })
    const {
      getFieldsValue,
      setFieldsValue,
      resetFields,
      validate,
      clearValidate,
      handleSubmit
    } = useFormEvents({
      emit,
      formModel,
      defaultValueRef,
      getSchema,
      formElRef: formElRef as Ref<FormActionType>
    })
    const formActionType: Partial<FormActionType> = {
      setProps,
      getFieldsValue,
      setFieldsValue,
      resetFields,
      validate,
      clearValidate,
      submit: handleSubmit
    }
    watch(
      () => getSchema.value,
      (schema) => {
        if (schema?.length) {
          initDefault()
        }
      }
    )
    onMounted(() => {
      initDefault()
      emit('register', formActionType)
    })
    return {
      formModel,
      setFormModel,
      defaultValueRef,
      getProps,
      formActionType,
      getSchema,
      formElRef,
      handleToggleAdvanced,
      advanceState,
      ...formActionType
    }
  }
})
</script>
